package filesource

import (
	"context"           // 提供上下文功能，用于控制监视器的生命周期（取消、超时）
	"gameServer/config" // 包含配置监视器接口（ConfigWatcher）及配置结构体（Config）定义
	"io/fs"             // 文件系统接口，用于目录遍历（如DirEntry）
	"os"                // 提供操作系统级别的文件操作（如Stat）
	"path/filepath"     // 路径处理工具，用于解析、拼接文件路径
	"strings"           // 字符串处理工具，用于过滤隐藏文件

	"github.com/fsnotify/fsnotify" // 文件系统监视库，用于监听文件/目录的变化事件（如写入、创建）
)

/* 文件监视器：实现config.ConfigWatcher接口，用于实时监听文件系统中配置文件的变化（如修改、创建），
当文件发生变化时，触发配置重新加载并通知配置管理器。 */

// fileWatcher 文件监视器结构体，实现了config.ConfigWatcher接口
// 负责通过fsnotify库监听指定路径（文件或目录）的变化，并将变化转换为配置对象
type fileWatcher struct {
	ctx        context.Context    // 上下文，用于控制监视器的生命周期（可被取消以停止监视）
	cancel     context.CancelFunc // 用于取消ctx的函数，调用后终止监视器的事件监听
	fileSource *FileSource        // 关联的文件配置源，用于在文件变化时加载最新配置内容
	watcher    *fsnotify.Watcher  // fsnotify库的监视器实例，底层负责监听文件系统事件
}

// newWatcher 创建一个新的文件监视器实例
// 功能：初始化fsnotify监视器，根据文件源的路径（文件/目录）添加监听目标，返回实现ConfigWatcher接口的实例
// 参数：
//
//	ctx: 父上下文，用于控制监视器的生命周期（可通过父ctx取消）
//	fileSource: 关联的文件配置源，提供要监视的路径及加载配置的方法
//
// 返回值：
//
//	config.ConfigWatcher: 初始化后的文件监视器（实现了接口）
//	error: 创建过程中发生的错误（如初始化fsnotify失败、添加监听路径失败等）
func newWatcher(ctx context.Context, fileSource *FileSource) (config.ConfigWatcher, error) {
	// 检查文件源的基础路径（fileSource.path）是否存在及状态（文件/目录）
	info, err := os.Stat(fileSource.path)
	if err != nil {
		return nil, err // 路径不存在或无法访问，返回错误
	}

	// 初始化fsnotify监视器实例，用于监听文件系统事件
	fsWatcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err // 初始化失败（如系统不支持），返回错误
	}

	// 根据路径类型（目录/文件）添加监听目标
	if info.IsDir() {
		// 路径是目录：递归遍历目录下所有文件和子目录，添加监听
		err = filepath.WalkDir(fileSource.path, func(path string, d fs.DirEntry, err error) error {
			// 若遍历过程中发生错误（如子目录无权限），返回错误终止遍历
			if err != nil {
				return err
			}
			// 过滤隐藏文件/目录（以"."开头，如".git"、".tmp"），不监听此类文件
			if strings.HasPrefix(d.Name(), ".") {
				return nil // 跳过当前条目，继续遍历
			}
			// 为当前路径（文件或目录）添加到fsnotify监视器中，开始监听其变化
			return fsWatcher.Add(path)
		})
	} else {
		// 路径是单个文件：直接将该文件添加到监视器中
		err = fsWatcher.Add(fileSource.path)
	}

	// 若添加监听路径失败，返回错误
	if err != nil {
		return nil, err
	}

	// 初始化fileWatcher结构体，关联文件源、fsnotify监视器
	watcher := &fileWatcher{
		fileSource: fileSource,
		watcher:    fsWatcher,
	}
	// 创建带取消功能的上下文（基于传入的ctx），用于控制监视器的生命周期
	watcher.ctx, watcher.cancel = context.WithCancel(ctx)

	// 返回实现了ConfigWatcher接口的监视器实例
	return watcher, nil
}

// Next 实现config.ConfigWatcher接口的Next方法，阻塞等待文件变化事件，并返回变化的配置
// 功能：监听fsnotify事件（如文件写入、创建），当事件发生时，通过关联的文件源加载最新配置，返回配置列表
// 返回值：
//
//	[]*config.Config: 变化的配置列表（通常包含一个变化的文件配置）
//	error: 监听过程中发生的错误（如监视器已关闭、上下文被取消等）
func (fw *fileWatcher) Next() ([]*config.Config, error) {
	// 阻塞等待三种事件：文件系统事件、错误、上下文取消
	select {
	case event, ok := <-fw.watcher.Events:
		// 接收fsnotify的文件系统事件（如写入、创建、删除等）
		// ok为false表示事件通道已关闭（监视器被关闭）
		if !ok {
			return nil, nil // 通道关闭，返回空结果
		}

		// 只处理"写入"（文件内容修改）和"创建"（新文件生成）事件，忽略删除、重命名等事件
		if event.Has(fsnotify.Write) || event.Has(fsnotify.Create) {
			// 通过关联的文件源加载变化文件的最新内容，转换为config.Config结构体
			cfg, err := fw.fileSource.loadFile(event.Name)
			if err != nil {
				return nil, err // 加载文件失败（如文件已删除），返回错误
			}
			// 返回包含变化配置的列表（单个文件变化）
			return []*config.Config{cfg}, nil
		}

	case err := <-fw.watcher.Errors:
		// 接收fsnotify监视器的错误（如监听路径被删除）
		return nil, err

	case <-fw.ctx.Done():
		// 上下文被取消（如调用Stop方法），返回上下文的错误（通常是context.Canceled）
		return nil, fw.ctx.Err()
	}

	// 未处理的事件（如删除、重命名），返回空结果
	return nil, nil
}

// Stop 实现config.ConfigWatcher接口的Stop方法，停止文件监视
// 功能：取消上下文，关闭fsnotify监视器，释放资源
// 返回值：
//
//	error: 关闭监视器过程中发生的错误（如已关闭）
func (fw *fileWatcher) Stop() error {
	// 调用cancel函数取消上下文，终止Next方法的阻塞等待
	fw.cancel()
	// 关闭fsnotify监视器，释放底层资源（如文件描述符）
	return fw.watcher.Close()
}
